<!--
  - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
  - SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
	<div class="sharingTabDetailsView">
		<div class="sharingTabDetailsView__header">
			<span>
				<NcAvatar
					v-if="isUserShare"
					class="sharing-entry__avatar"
					:is-no-user="share.shareType !== ShareType.User"
					:user="share.shareWith"
					:display-name="share.shareWithDisplayName"
					menu-position="left"
					:url="share.shareWithAvatar" />
				<component :is="getShareTypeIcon(share.type)" :size="32" />
			</span>
			<span>
				<h1>{{ title }}</h1>
			</span>
		</div>
		<div class="sharingTabDetailsView__wrapper">
			<div ref="quickPermissions" class="sharingTabDetailsView__quick-permissions">
				<div>
					<NcCheckboxRadioSwitch
						v-model="sharingPermission"
						:button-variant="true"
						data-cy-files-sharing-share-permissions-bundle="read-only"
						:value="bundledPermissions.READ_ONLY.toString()"
						name="sharing_permission_radio"
						type="radio"
						button-variant-grouped="vertical"
						@update:modelValue="toggleCustomPermissions">
						{{ t('files_sharing', 'View only') }}
						<template #icon>
							<ViewIcon :size="20" />
						</template>
					</NcCheckboxRadioSwitch>
					<NcCheckboxRadioSwitch
						v-model="sharingPermission"
						:button-variant="true"
						data-cy-files-sharing-share-permissions-bundle="upload-edit"
						:value="allPermissions"
						name="sharing_permission_radio"
						type="radio"
						button-variant-grouped="vertical"
						@update:modelValue="toggleCustomPermissions">
						<template v-if="allowsFileDrop">
							{{ t('files_sharing', 'Allow upload and editing') }}
						</template>
						<template v-else>
							{{ t('files_sharing', 'Allow editing') }}
						</template>
						<template #icon>
							<EditIcon :size="20" />
						</template>
					</NcCheckboxRadioSwitch>
					<NcCheckboxRadioSwitch
						v-if="allowsFileDrop"
						v-model="sharingPermission"
						data-cy-files-sharing-share-permissions-bundle="file-drop"
						:button-variant="true"
						:value="bundledPermissions.FILE_DROP.toString()"
						name="sharing_permission_radio"
						type="radio"
						button-variant-grouped="vertical"
						@update:modelValue="toggleCustomPermissions">
						{{ t('files_sharing', 'File request') }}
						<small class="subline">{{ t('files_sharing', 'Upload only') }}</small>
						<template #icon>
							<UploadIcon :size="20" />
						</template>
					</NcCheckboxRadioSwitch>
					<NcCheckboxRadioSwitch
						v-model="sharingPermission"
						:button-variant="true"
						data-cy-files-sharing-share-permissions-bundle="custom"
						value="custom"
						name="sharing_permission_radio"
						type="radio"
						button-variant-grouped="vertical"
						@update:modelValue="expandCustomPermissions">
						{{ t('files_sharing', 'Custom permissions') }}
						<small class="subline">{{ customPermissionsList }}</small>
						<template #icon>
							<DotsHorizontalIcon :size="20" />
						</template>
					</NcCheckboxRadioSwitch>
				</div>
			</div>
			<div class="sharingTabDetailsView__advanced-control">
				<NcButton
					id="advancedSectionAccordionAdvancedControl"
					variant="tertiary"
					alignment="end-reverse"
					aria-controls="advancedSectionAccordionAdvanced"
					:aria-expanded="advancedControlExpandedValue"
					@click="advancedSectionAccordionExpanded = !advancedSectionAccordionExpanded">
					{{ t('files_sharing', 'Advanced settings') }}
					<template #icon>
						<MenuDownIcon v-if="!advancedSectionAccordionExpanded" />
						<MenuUpIcon v-else />
					</template>
				</NcButton>
			</div>
			<div
				v-if="advancedSectionAccordionExpanded"
				id="advancedSectionAccordionAdvanced"
				class="sharingTabDetailsView__advanced"
				aria-labelledby="advancedSectionAccordionAdvancedControl"
				role="region">
				<section>
					<NcInputField
						v-if="isPublicShare"
						v-model="share.label"
						class="sharingTabDetailsView__label"
						autocomplete="off"
						:label="t('files_sharing', 'Share label')" />
					<NcInputField
						v-if="config.allowCustomTokens && isPublicShare && !isNewShare"
						v-model="share.token"
						autocomplete="off"
						:label="t('files_sharing', 'Share link token')"
						:helper-text="t('files_sharing', 'Set the public share link token to something easy to remember or generate a new token. It is not recommended to use a guessable token for shares which contain sensitive information.')"
						show-trailing-button
						:trailing-button-label="loadingToken ? t('files_sharing', 'Generating…') : t('files_sharing', 'Generate new token')"
						@trailing-button-click="generateNewToken">
						<template #trailing-button-icon>
							<NcLoadingIcon v-if="loadingToken" />
							<Refresh v-else :size="20" />
						</template>
					</NcInputField>
					<template v-if="isPublicShare">
						<NcCheckboxRadioSwitch v-model="isPasswordProtected" :disabled="isPasswordEnforced">
							{{ t('files_sharing', 'Set password') }}
						</NcCheckboxRadioSwitch>
						<NcPasswordField
							v-if="isPasswordProtected"
							autocomplete="new-password"
							:model-value="share.newPassword ?? ''"
							:error="passwordError"
							:helper-text="errorPasswordLabel || passwordHint"
							:required="isPasswordEnforced && isNewShare"
							:label="t('files_sharing', 'Password')"
							@update:value="onPasswordChange" />

						<!-- Migrate icons and remote -> icon="icon-info"-->
						<span v-if="isEmailShareType && passwordExpirationTime" icon="icon-info">
							{{ t('files_sharing', 'Password expires {passwordExpirationTime}', { passwordExpirationTime }) }}
						</span>
						<span v-else-if="isEmailShareType && passwordExpirationTime !== null" icon="icon-error">
							{{ t('files_sharing', 'Password expired') }}
						</span>
					</template>
					<NcCheckboxRadioSwitch
						v-if="canTogglePasswordProtectedByTalkAvailable"
						v-model="isPasswordProtectedByTalk"
						@update:modelValue="onPasswordProtectedByTalkChange">
						{{ t('files_sharing', 'Video verification') }}
					</NcCheckboxRadioSwitch>
					<NcCheckboxRadioSwitch v-model="hasExpirationDate" :disabled="isExpiryDateEnforced">
						{{ isExpiryDateEnforced
							? t('files_sharing', 'Expiration date (enforced)')
							: t('files_sharing', 'Set expiration date') }}
					</NcCheckboxRadioSwitch>
					<NcDateTimePickerNative
						v-if="hasExpirationDate"
						id="share-date-picker"
						:model-value="new Date(share.expireDate ?? dateTomorrow)"
						:min="dateTomorrow"
						:max="maxExpirationDateEnforced"
						hide-label
						:label="t('files_sharing', 'Expiration date')"
						:placeholder="t('files_sharing', 'Expiration date')"
						type="date"
						@input="onExpirationChange" />
					<NcCheckboxRadioSwitch
						v-if="isPublicShare"
						v-model="share.hideDownload"
						:disabled="canChangeHideDownload"
						@update:modelValue="queueUpdate('hideDownload')">
						{{ t('files_sharing', 'Hide download') }}
					</NcCheckboxRadioSwitch>
					<NcCheckboxRadioSwitch
						v-else
						v-model="canDownload"
						:disabled="!canSetDownload"
						data-cy-files-sharing-share-permissions-checkbox="download">
						{{ t('files_sharing', 'Allow download and sync') }}
					</NcCheckboxRadioSwitch>
					<NcCheckboxRadioSwitch v-model="writeNoteToRecipientIsChecked">
						{{ t('files_sharing', 'Note to recipient') }}
					</NcCheckboxRadioSwitch>
					<template v-if="writeNoteToRecipientIsChecked">
						<NcTextArea
							v-model="share.note"
							:label="t('files_sharing', 'Note to recipient')"
							:placeholder="t('files_sharing', 'Enter a note for the share recipient')" />
					</template>
					<NcCheckboxRadioSwitch
						v-if="isPublicShare && isFolder"
						v-model="showInGridView">
						{{ t('files_sharing', 'Show files in grid view') }}
					</NcCheckboxRadioSwitch>

					<SidebarTabExternalAction
						v-for="action in sortedExternalShareActions"
						:key="action.id"
						ref="externalShareActions"
						:action="action"
						:node="fileInfo.node /* TODO: Fix once we have proper Node API */"
						:share="share" />
					<SidebarTabExternalActionLegacy
						v-for="action in externalLegacyShareActions"
						:id="action.id"
						ref="externalLinkActions"
						:key="action.id"
						:action="action"
						:file-info="fileInfo"
						:share="share" />

					<NcCheckboxRadioSwitch v-model="setCustomPermissions">
						{{ t('files_sharing', 'Custom permissions') }}
					</NcCheckboxRadioSwitch>
					<section v-if="setCustomPermissions" class="custom-permissions-group">
						<NcCheckboxRadioSwitch
							v-model="hasRead"
							:disabled="!canRemoveReadPermission"
							data-cy-files-sharing-share-permissions-checkbox="read">
							{{ t('files_sharing', 'Read') }}
						</NcCheckboxRadioSwitch>
						<NcCheckboxRadioSwitch
							v-if="isFolder"
							v-model="canCreate"
							:disabled="!canSetCreate"
							data-cy-files-sharing-share-permissions-checkbox="create">
							{{ t('files_sharing', 'Create') }}
						</NcCheckboxRadioSwitch>
						<NcCheckboxRadioSwitch
							v-model="canEdit"
							:disabled="!canSetEdit"
							data-cy-files-sharing-share-permissions-checkbox="update">
							{{ t('files_sharing', 'Edit') }}
						</NcCheckboxRadioSwitch>
						<NcCheckboxRadioSwitch
							v-if="resharingIsPossible"
							v-model="canReshare"
							:disabled="!canSetReshare"
							data-cy-files-sharing-share-permissions-checkbox="share">
							{{ t('files_sharing', 'Share') }}
						</NcCheckboxRadioSwitch>
						<NcCheckboxRadioSwitch
							v-model="canDelete"
							:disabled="!canSetDelete"
							data-cy-files-sharing-share-permissions-checkbox="delete">
							{{ t('files_sharing', 'Delete') }}
						</NcCheckboxRadioSwitch>
					</section>
				</section>
			</div>
		</div>

		<div class="sharingTabDetailsView__footer">
			<div class="button-group">
				<NcButton
					data-cy-files-sharing-share-editor-action="cancel"
					@click="cancel">
					{{ t('files_sharing', 'Cancel') }}
				</NcButton>
				<div class="sharingTabDetailsView__delete">
					<NcButton
						v-if="!isNewShare"
						:aria-label="t('files_sharing', 'Delete share')"
						:disabled="false"
						:readonly="false"
						variant="tertiary"
						@click.prevent="removeShare">
						<template #icon>
							<CloseIcon :size="20" />
						</template>
						{{ t('files_sharing', 'Delete share') }}
					</NcButton>
				</div>
				<NcButton
					variant="primary"
					data-cy-files-sharing-share-editor-action="save"
					:disabled="creating"
					@click="saveShare">
					{{ shareButtonText }}
					<template v-if="creating" #icon>
						<NcLoadingIcon />
					</template>
				</NcButton>
			</div>
		</div>
	</div>
</template>

<script>
import { showError } from '@nextcloud/dialogs'
import { emit } from '@nextcloud/event-bus'
import { getLanguage } from '@nextcloud/l10n'
import moment from '@nextcloud/moment'
import { ShareType } from '@nextcloud/sharing'
import { getSidebarActions } from '@nextcloud/sharing/ui'
import { toRaw } from 'vue'
import NcAvatar from '@nextcloud/vue/components/NcAvatar'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
import NcDateTimePickerNative from '@nextcloud/vue/components/NcDateTimePickerNative'
import NcInputField from '@nextcloud/vue/components/NcInputField'
import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
import NcPasswordField from '@nextcloud/vue/components/NcPasswordField'
import NcTextArea from '@nextcloud/vue/components/NcTextArea'
import UserIcon from 'vue-material-design-icons/AccountCircleOutline.vue'
import GroupIcon from 'vue-material-design-icons/AccountGroup.vue'
import CircleIcon from 'vue-material-design-icons/CircleOutline.vue'
import CloseIcon from 'vue-material-design-icons/Close.vue'
import DotsHorizontalIcon from 'vue-material-design-icons/DotsHorizontal.vue'
import EmailIcon from 'vue-material-design-icons/Email.vue'
import ViewIcon from 'vue-material-design-icons/Eye.vue'
import LinkIcon from 'vue-material-design-icons/Link.vue'
import MenuDownIcon from 'vue-material-design-icons/MenuDown.vue'
import MenuUpIcon from 'vue-material-design-icons/MenuUp.vue'
import EditIcon from 'vue-material-design-icons/PencilOutline.vue'
import Refresh from 'vue-material-design-icons/Refresh.vue'
import ShareIcon from 'vue-material-design-icons/ShareCircle.vue'
import UploadIcon from 'vue-material-design-icons/TrayArrowUp.vue'
import SidebarTabExternalAction from '../components/SidebarTabExternal/SidebarTabExternalAction.vue'
import SidebarTabExternalActionLegacy from '../components/SidebarTabExternal/SidebarTabExternalActionLegacy.vue'
import {
	ATOMIC_PERMISSIONS,
	BUNDLED_PERMISSIONS,
	hasPermissions,
} from '../lib/SharePermissionsToolBox.js'
import ShareRequests from '../mixins/ShareRequests.js'
import SharesMixin from '../mixins/SharesMixin.js'
import logger from '../services/logger.ts'
import { generateToken } from '../services/TokenService.ts'
import GeneratePassword from '../utils/GeneratePassword.ts'

/** @typedef {import('../models/Share.js').default} Share */
export default {
	name: 'SharingDetailsTab',
	components: {
		NcAvatar,
		NcButton,
		NcCheckboxRadioSwitch,
		NcDateTimePickerNative,
		NcInputField,
		NcLoadingIcon,
		NcPasswordField,
		NcTextArea,
		CloseIcon,
		CircleIcon,
		EditIcon,
		LinkIcon,
		GroupIcon,
		ShareIcon,
		UserIcon,
		UploadIcon,
		ViewIcon,
		MenuDownIcon,
		MenuUpIcon,
		DotsHorizontalIcon,
		Refresh,
		SidebarTabExternalAction,
		SidebarTabExternalActionLegacy,
	},

	mixins: [ShareRequests, SharesMixin],
	props: {
		shareRequestValue: {
			type: Object,
			required: false,
		},

		fileInfo: {
			type: Object,
			required: true,
		},

		share: {
			type: Object,
			required: true,
		},
	},

	data() {
		return {
			writeNoteToRecipientIsChecked: false,
			sharingPermission: BUNDLED_PERMISSIONS.ALL.toString(),
			revertSharingPermission: BUNDLED_PERMISSIONS.ALL.toString(),
			setCustomPermissions: false,
			passwordError: false,
			advancedSectionAccordionExpanded: false,
			bundledPermissions: BUNDLED_PERMISSIONS,
			isFirstComponentLoad: true,
			test: false,
			creating: false,
			initialToken: this.share.token,
			loadingToken: false,

			externalShareActions: getSidebarActions(),
			// legacy
			ExternalShareActions: OCA.Sharing.ExternalShareActions.state,
		}
	},

	computed: {
		title() {
			switch (this.share.type) {
				case ShareType.User:
					return t('files_sharing', 'Share with {user}', { user: this.share.shareWithDisplayName })
				case ShareType.Email:
					return t('files_sharing', 'Share with email {email}', { email: this.share.shareWith })
				case ShareType.Link:
					return t('files_sharing', 'Share link')
				case ShareType.Group:
					return t('files_sharing', 'Share with group')
				case ShareType.Room:
					return t('files_sharing', 'Share in conversation')
				case ShareType.Remote: {
					const [user, server] = this.share.shareWith.split('@')
					if (this.config.showFederatedSharesAsInternal) {
						return t('files_sharing', 'Share with {user}', { user })
					}
					return t('files_sharing', 'Share with {user} on remote server {server}', { user, server })
				}
				case ShareType.RemoteGroup:
					return t('files_sharing', 'Share with remote group')
				case ShareType.Guest:
					return t('files_sharing', 'Share with guest')
				default: {
					if (this.share.id) {
					// Share already exists
						return t('files_sharing', 'Update share')
					} else {
						return t('files_sharing', 'Create share')
					}
				}
			}
		},

		allPermissions() {
			return this.isFolder ? this.bundledPermissions.ALL.toString() : this.bundledPermissions.ALL_FILE.toString()
		},

		/**
		 * Can the sharee edit the shared file ?
		 */
		canEdit: {
			get() {
				return this.share.hasUpdatePermission
			},

			set(checked) {
				this.updateAtomicPermissions({ isEditChecked: checked })
			},
		},

		/**
		 * Can the sharee create the shared file ?
		 */
		canCreate: {
			get() {
				return this.share.hasCreatePermission
			},

			set(checked) {
				this.updateAtomicPermissions({ isCreateChecked: checked })
			},
		},

		/**
		 * Can the sharee delete the shared file ?
		 */
		canDelete: {
			get() {
				return this.share.hasDeletePermission
			},

			set(checked) {
				this.updateAtomicPermissions({ isDeleteChecked: checked })
			},
		},

		/**
		 * Can the sharee reshare the file ?
		 */
		canReshare: {
			get() {
				return this.share.hasSharePermission
			},

			set(checked) {
				this.updateAtomicPermissions({ isReshareChecked: checked })
			},
		},

		/**
		 * Change the default view for public shares from "list" to "grid"
		 */
		showInGridView: {
			get() {
				return this.getShareAttribute('config', 'grid_view', false)
			},

			/** @param {boolean} value If the default view should be changed to "grid" */
			set(value) {
				this.setShareAttribute('config', 'grid_view', value)
			},
		},

		/**
		 * Can the sharee download files or only view them ?
		 */
		canDownload: {
			get() {
				return this.getShareAttribute('permissions', 'download', true)
			},

			set(checked) {
				this.setShareAttribute('permissions', 'download', checked)
			},
		},

		/**
		 * Is this share readable
		 * Needed for some federated shares that might have been added from file requests links
		 */
		hasRead: {
			get() {
				return this.share.hasReadPermission
			},

			set(checked) {
				this.updateAtomicPermissions({ isReadChecked: checked })
			},
		},

		/**
		 * Does the current share have an expiration date
		 *
		 * @return {boolean}
		 */
		hasExpirationDate: {
			get() {
				return this.isValidShareAttribute(this.share.expireDate)
			},

			set(enabled) {
				this.share.expireDate = enabled
					? this.formatDateToString(this.defaultExpiryDate)
					: ''
			},
		},

		/**
		 * Is the current share a folder ?
		 *
		 * @return {boolean}
		 */
		isFolder() {
			return this.fileInfo.type === 'dir'
		},

		/**
		 * @return {boolean}
		 */
		isSetDownloadButtonVisible() {
			const allowedMimetypes = [
				// Office documents
				'application/msword',
				'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
				'application/vnd.ms-powerpoint',
				'application/vnd.openxmlformats-officedocument.presentationml.presentation',
				'application/vnd.ms-excel',
				'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
				'application/vnd.oasis.opendocument.text',
				'application/vnd.oasis.opendocument.spreadsheet',
				'application/vnd.oasis.opendocument.presentation',
			]

			return this.isFolder || allowedMimetypes.includes(this.fileInfo.mimetype)
		},

		isPasswordEnforced() {
			return this.isPublicShare && this.config.enforcePasswordForPublicLink
		},

		defaultExpiryDate() {
			if ((this.isGroupShare || this.isUserShare) && this.config.isDefaultInternalExpireDateEnabled) {
				return new Date(this.config.defaultInternalExpirationDate)
			} else if (this.isRemoteShare && this.config.isDefaultRemoteExpireDateEnabled) {
				return new Date(this.config.defaultRemoteExpireDateEnabled)
			} else if (this.isPublicShare && this.config.isDefaultExpireDateEnabled) {
				return new Date(this.config.defaultExpirationDate)
			}
			return new Date(new Date().setDate(new Date().getDate() + 1))
		},

		isUserShare() {
			return this.share.type === ShareType.User
		},

		isGroupShare() {
			return this.share.type === ShareType.Group
		},

		allowsFileDrop() {
			if (this.isFolder && this.config.isPublicUploadEnabled) {
				if (this.share.type === ShareType.Link || this.share.type === ShareType.Email) {
					return true
				}
			}
			return false
		},

		hasFileDropPermissions() {
			return this.share.permissions === this.bundledPermissions.FILE_DROP
		},

		shareButtonText() {
			if (this.isNewShare) {
				return t('files_sharing', 'Save share')
			}
			return t('files_sharing', 'Update share')
		},

		resharingIsPossible() {
			return this.config.isResharingAllowed && this.share.type !== ShareType.Link && this.share.type !== ShareType.Email
		},

		/**
		 * Can the sharer set whether the sharee can edit the file ?
		 *
		 * @return {boolean}
		 */
		canSetEdit() {
			// If the owner revoked the permission after the resharer granted it
			// the share still has the permission, and the resharer is still
			// allowed to revoke it too (but not to grant it again).
			return (this.fileInfo.sharePermissions & OC.PERMISSION_UPDATE) || this.canEdit
		},

		/**
		 * Can the sharer set whether the sharee can create the file ?
		 *
		 * @return {boolean}
		 */
		canSetCreate() {
			// If the owner revoked the permission after the resharer granted it
			// the share still has the permission, and the resharer is still
			// allowed to revoke it too (but not to grant it again).
			return (this.fileInfo.sharePermissions & OC.PERMISSION_CREATE) || this.canCreate
		},

		/**
		 * Can the sharer set whether the sharee can delete the file ?
		 *
		 * @return {boolean}
		 */
		canSetDelete() {
			// If the owner revoked the permission after the resharer granted it
			// the share still has the permission, and the resharer is still
			// allowed to revoke it too (but not to grant it again).
			return (this.fileInfo.sharePermissions & OC.PERMISSION_DELETE) || this.canDelete
		},

		/**
		 * Can the sharer set whether the sharee can reshare the file ?
		 *
		 * @return {boolean}
		 */
		canSetReshare() {
			// If the owner revoked the permission after the resharer granted it
			// the share still has the permission, and the resharer is still
			// allowed to revoke it too (but not to grant it again).
			return (this.fileInfo.sharePermissions & OC.PERMISSION_SHARE) || this.canReshare
		},

		/**
		 * Can the sharer set whether the sharee can download the file ?
		 *
		 * @return {boolean}
		 */
		canSetDownload() {
			// If the owner revoked the permission after the resharer granted it
			// the share still has the permission, and the resharer is still
			// allowed to revoke it too (but not to grant it again).
			return (this.fileInfo.canDownload() || this.canDownload)
		},

		canRemoveReadPermission() {
			return this.allowsFileDrop && (
				this.share.type === ShareType.Link
				|| this.share.type === ShareType.Email
			)
		},

		// if newPassword exists, but is empty, it means
		// the user deleted the original password
		hasUnsavedPassword() {
			return this.share.newPassword !== undefined
		},

		passwordExpirationTime() {
			if (!this.isValidShareAttribute(this.share.passwordExpirationTime)) {
				return null
			}

			const expirationTime = moment(this.share.passwordExpirationTime)

			if (expirationTime.diff(moment()) < 0) {
				return false
			}

			return expirationTime.fromNow()
		},

		/**
		 * Is Talk enabled?
		 *
		 * @return {boolean}
		 */
		isTalkEnabled() {
			return OC.appswebroots.spreed !== undefined
		},

		/**
		 * Is it possible to protect the password by Talk?
		 *
		 * @return {boolean}
		 */
		isPasswordProtectedByTalkAvailable() {
			return this.isPasswordProtected && this.isTalkEnabled
		},

		/**
		 * Is the current share password protected by Talk?
		 *
		 * @return {boolean}
		 */
		isPasswordProtectedByTalk: {
			get() {
				return this.share.sendPasswordByTalk
			},

			async set(enabled) {
				this.share.sendPasswordByTalk = enabled
			},
		},

		/**
		 * Is the current share an email share ?
		 *
		 * @return {boolean}
		 */
		isEmailShareType() {
			return this.share
				? this.share.type === ShareType.Email
				: false
		},

		canTogglePasswordProtectedByTalkAvailable() {
			if (!this.isPublicShare || !this.isPasswordProtected) {
				// Makes no sense
				return false
			} else if (this.isEmailShareType && !this.hasUnsavedPassword) {
				// For email shares we need a new password in order to enable or
				// disable
				return false
			}

			// Is Talk enabled?
			return OC.appswebroots.spreed !== undefined
		},

		canChangeHideDownload() {
			const hasDisabledDownload = (shareAttribute) => shareAttribute.key === 'download' && shareAttribute.scope === 'permissions' && shareAttribute.value === false
			return this.fileInfo.shareAttributes.some(hasDisabledDownload)
		},

		customPermissionsList() {
			// Key order will be different, because ATOMIC_PERMISSIONS are numbers
			const translatedPermissions = {
				[ATOMIC_PERMISSIONS.READ]: this.t('files_sharing', 'Read'),
				[ATOMIC_PERMISSIONS.CREATE]: this.t('files_sharing', 'Create'),
				[ATOMIC_PERMISSIONS.UPDATE]: this.t('files_sharing', 'Edit'),
				[ATOMIC_PERMISSIONS.SHARE]: this.t('files_sharing', 'Share'),
				[ATOMIC_PERMISSIONS.DELETE]: this.t('files_sharing', 'Delete'),
			}

			const permissionsList = [
				ATOMIC_PERMISSIONS.READ,
				...(this.isFolder ? [ATOMIC_PERMISSIONS.CREATE] : []),
				ATOMIC_PERMISSIONS.UPDATE,
				...(this.resharingIsPossible ? [ATOMIC_PERMISSIONS.SHARE] : []),
				...(this.isFolder ? [ATOMIC_PERMISSIONS.DELETE] : []),
			]

			return permissionsList.filter((permission) => hasPermissions(this.share.permissions, permission))
				.map((permission, index) => index === 0
					? translatedPermissions[permission]
					: translatedPermissions[permission].toLocaleLowerCase(getLanguage()))
				.join(', ')
		},

		advancedControlExpandedValue() {
			return this.advancedSectionAccordionExpanded ? 'true' : 'false'
		},

		errorPasswordLabel() {
			if (this.passwordError) {
				return t('files_sharing', 'Password field cannot be empty')
			}
			return undefined
		},

		passwordHint() {
			if (this.isNewShare || this.hasUnsavedPassword) {
				return undefined
			}
			return t('files_sharing', 'Replace current password')
		},

		/**
		 * Additional actions for the menu
		 *
		 * @return {Array}
		 */
		sortedExternalShareActions() {
			return this.externalShareActions
				.filter((action) => action.enabled(toRaw(this.share), toRaw(this.fileInfo.node)))
				.sort((a, b) => a.order - b.order)
		},

		/**
		 * Additional actions for the menu
		 *
		 * @return {Array}
		 */
		externalLegacyShareActions() {
			const filterValidAction = (action) => (action.shareType.includes(ShareType.Link) || action.shareType.includes(ShareType.Email)) && action.advanced
			logger.debug('legacy details tab', {
				ExternalShareActions: this.ExternalShareActions,
			})
			// filter only the advanced registered actions for said link
			return this.ExternalShareActions.actions
				.filter(filterValidAction)
		},
	},

	watch: {
		setCustomPermissions(isChecked) {
			if (isChecked) {
				this.sharingPermission = 'custom'
			} else {
				this.sharingPermission = this.revertSharingPermission
			}
		},
	},

	beforeMount() {
		this.initializePermissions()
		this.initializeAttributes()
		logger.debug('Share object received', { share: this.share })
		logger.debug('Configuration object received', { config: this.config })
	},

	mounted() {
		this.$refs.quickPermissions?.querySelector('input:checked')?.focus()
	},

	methods: {
		/**
		 * Set a share attribute on the current share
		 *
		 * @param {string} scope The attribute scope
		 * @param {string} key The attribute key
		 * @param {boolean} value The value
		 */
		setShareAttribute(scope, key, value) {
			if (!this.share.attributes) {
				this.$set(this.share, 'attributes', [])
			}

			const attribute = this.share.attributes
				.find((attr) => attr.scope === scope || attr.key === key)

			if (attribute) {
				attribute.value = value
			} else {
				this.share.attributes.push({
					scope,
					key,
					value,
				})
			}
		},

		/**
		 * Get the value of a share attribute
		 *
		 * @param {string} scope The attribute scope
		 * @param {string} key The attribute key
		 * @param {undefined|boolean} fallback The fallback to return if not found
		 */
		getShareAttribute(scope, key, fallback = undefined) {
			const attribute = this.share.attributes?.find((attr) => attr.scope === scope && attr.key === key)
			return attribute?.value ?? fallback
		},

		async generateNewToken() {
			if (this.loadingToken) {
				return
			}
			this.loadingToken = true
			try {
				this.share.token = await generateToken()
			} catch {
				showError(t('files_sharing', 'Failed to generate a new token'))
			}
			this.loadingToken = false
		},

		cancel() {
			this.share.token = this.initialToken
			this.$emit('close-sharing-details')
		},

		updateAtomicPermissions({
			isReadChecked = this.hasRead,
			isEditChecked = this.canEdit,
			isCreateChecked = this.canCreate,
			isDeleteChecked = this.canDelete,
			isReshareChecked = this.canReshare,
		} = {}) {
			// calc permissions if checked

			if (!this.isFolder && (isCreateChecked || isDeleteChecked)) {
				logger.debug('Ignoring create/delete permissions for file share — only available for folders')
				isCreateChecked = false
				isDeleteChecked = false
			}

			const permissions = 0
				| (isReadChecked ? ATOMIC_PERMISSIONS.READ : 0)
				| (isCreateChecked ? ATOMIC_PERMISSIONS.CREATE : 0)
				| (isDeleteChecked ? ATOMIC_PERMISSIONS.DELETE : 0)
				| (isEditChecked ? ATOMIC_PERMISSIONS.UPDATE : 0)
				| (isReshareChecked ? ATOMIC_PERMISSIONS.SHARE : 0)
			this.share.permissions = permissions
		},

		expandCustomPermissions() {
			if (!this.advancedSectionAccordionExpanded) {
				this.advancedSectionAccordionExpanded = true
			}
			this.toggleCustomPermissions()
		},

		toggleCustomPermissions(selectedPermission) {
			const isCustomPermissions = this.sharingPermission === 'custom'
			this.revertSharingPermission = !isCustomPermissions ? selectedPermission : 'custom'
			this.setCustomPermissions = isCustomPermissions
		},

		async initializeAttributes() {
			if (this.isNewShare) {
				if ((this.config.enableLinkPasswordByDefault || this.isPasswordEnforced) && this.isPublicShare) {
					this.$set(this.share, 'newPassword', await GeneratePassword(true))
					this.advancedSectionAccordionExpanded = true
				}
				/* Set default expiration dates if configured */
				if (this.isPublicShare && this.config.isDefaultExpireDateEnabled) {
					this.share.expireDate = this.config.defaultExpirationDate.toDateString()
				} else if (this.isRemoteShare && this.config.isDefaultRemoteExpireDateEnabled) {
					this.share.expireDate = this.config.defaultRemoteExpirationDateString.toDateString()
				} else if (this.config.isDefaultInternalExpireDateEnabled) {
					this.share.expireDate = this.config.defaultInternalExpirationDate.toDateString()
				}

				if (this.isValidShareAttribute(this.share.expireDate)) {
					this.advancedSectionAccordionExpanded = true
				}

				return
			}

			// If there is an enforced expiry date, then existing shares created before enforcement
			// have no expiry date, hence we set it here.
			if (!this.isValidShareAttribute(this.share.expireDate) && this.isExpiryDateEnforced) {
				this.hasExpirationDate = true
			}

			if (
				this.isValidShareAttribute(this.share.password)
				|| this.isValidShareAttribute(this.share.expireDate)
				|| this.isValidShareAttribute(this.share.label)
			) {
				this.advancedSectionAccordionExpanded = true
			}

			if (this.isValidShareAttribute(this.share.note)) {
				this.writeNoteToRecipientIsChecked = true
				this.advancedSectionAccordionExpanded = true
			}
		},

		handleShareType() {
			if ('shareType' in this.share) {
				this.share.type = this.share.shareType
			} else if (this.share.share_type) {
				this.share.type = this.share.share_type
			}
		},

		handleDefaultPermissions() {
			if (this.isNewShare) {
				const defaultPermissions = this.config.defaultPermissions
				if (defaultPermissions === BUNDLED_PERMISSIONS.READ_ONLY || defaultPermissions === BUNDLED_PERMISSIONS.ALL) {
					this.sharingPermission = defaultPermissions.toString()
				} else {
					this.sharingPermission = 'custom'
					this.share.permissions = defaultPermissions
					this.advancedSectionAccordionExpanded = true
					this.setCustomPermissions = true
				}
			}
			// Read permission required for share creation
			if (!this.canRemoveReadPermission) {
				this.hasRead = true
			}
		},

		handleCustomPermissions() {
			if (!this.isNewShare && (this.hasCustomPermissions || this.share.setCustomPermissions)) {
				this.sharingPermission = 'custom'
				this.advancedSectionAccordionExpanded = true
				this.setCustomPermissions = true
			} else if (this.share.permissions) {
				this.sharingPermission = this.share.permissions.toString()
			}
		},

		initializePermissions() {
			this.handleShareType()
			this.handleDefaultPermissions()
			this.handleCustomPermissions()
		},

		async saveShare() {
			const permissionsAndAttributes = ['permissions', 'attributes', 'note', 'expireDate']
			const publicShareAttributes = ['label', 'hideDownload']
			// Only include password if it's being actively changed
			if (this.hasUnsavedPassword) {
				publicShareAttributes.push('password')
			}
			if (this.config.allowCustomTokens) {
				publicShareAttributes.push('token')
			}
			if (this.isPublicShare) {
				permissionsAndAttributes.push(...publicShareAttributes)
			}
			const sharePermissionsSet = parseInt(this.sharingPermission)
			if (this.setCustomPermissions) {
				this.updateAtomicPermissions()
			} else {
				this.share.permissions = sharePermissionsSet
			}

			if (!this.isFolder && this.share.permissions === BUNDLED_PERMISSIONS.ALL) {
				// It's not possible to create an existing file.
				this.share.permissions = BUNDLED_PERMISSIONS.ALL_FILE
			}
			if (!this.writeNoteToRecipientIsChecked) {
				this.share.note = ''
			}
			if (this.isPasswordProtected) {
				if (this.isPasswordEnforced && this.isNewShare && !this.isValidShareAttribute(this.share.newPassword)) {
					this.passwordError = true
				}
			} else {
				this.share.password = ''
			}

			if (!this.hasExpirationDate) {
				this.share.expireDate = ''
			}

			if (this.isNewShare) {
				const incomingShare = {
					permissions: this.share.permissions,
					shareType: this.share.type,
					shareWith: this.share.shareWith,
					attributes: this.share.attributes,
					note: this.share.note,
					fileInfo: this.fileInfo,
				}

				incomingShare.expireDate = this.hasExpirationDate ? this.share.expireDate : ''

				if (this.isPasswordProtected) {
					incomingShare.password = this.share.newPassword
				}

				let share
				try {
					this.creating = true
					share = await this.addShare(incomingShare)
				} catch {
					this.creating = false
					// Error is already handled by ShareRequests mixin
					return
				}

				// ugly hack to make code work - we need the id to be set but at the same time we need to keep values we want to update
				this.share._share.id = share.id
				await this.queueUpdate(...permissionsAndAttributes)
				// Also a ugly hack to update the updated permissions
				for (const prop of permissionsAndAttributes) {
					if (prop in share && prop in this.share) {
						try {
							share[prop] = this.share[prop]
						} catch {
							share._share[prop] = this.share[prop]
						}
					}
				}

				this.share = share
				this.creating = false
				this.$emit('add:share', this.share)
			} else {
				// Let's update after creation as some attrs are only available after creation
				await this.queueUpdate(...permissionsAndAttributes)
				this.$emit('update:share', this.share)
			}

			await this.getNode()
			emit('files:node:updated', this.node)

			if (this.$refs.externalShareActions?.length > 0) {
				/** @type {import('vue').ComponentPublicInstance<SidebarTabExternalAction>[]} */
				const actions = this.$refs.externalShareActions
				await Promise.allSettled(actions.map((action) => action.save()))
			}

			if (this.$refs.externalLinkActions?.length > 0) {
				await Promise.allSettled(this.$refs.externalLinkActions.map((action) => {
					if (typeof action.$children.at(0)?.onSave !== 'function') {
						return Promise.resolve()
					}
					return action.$children.at(0)?.onSave?.()
				}))
			}

			this.$emit('close-sharing-details')
		},

		/**
		 * Process the new share request
		 *
		 * @param {Share} share incoming share object
		 */
		async addShare(share) {
			logger.debug('Adding a new share from the input for', { share })
			const path = this.path
			try {
				const resultingShare = await this.createShare({
					path,
					shareType: share.shareType,
					shareWith: share.shareWith,
					permissions: share.permissions,
					expireDate: share.expireDate,
					attributes: JSON.stringify(share.attributes),
					...(share.note ? { note: share.note } : {}),
					...(share.password ? { password: share.password } : {}),
				})
				return resultingShare
			} catch (error) {
				logger.error('Error while adding new share', { error })
				throw error
			} finally {
				// this.loading = false // No loader here yet
			}
		},

		async removeShare() {
			await this.onDelete()
			await this.getNode()
			emit('files:node:updated', this.node)
			this.$emit('close-sharing-details')
		},

		/**
		 * Update newPassword values
		 * of share. If password is set but not newPassword
		 * then the user did not changed the password
		 * If both co-exists, the password have changed and
		 * we show it in plain text.
		 * Then on submit (or menu close), we sync it.
		 *
		 * @param {string} password the changed password
		 */
		onPasswordChange(password) {
			if (password === '') {
				this.$delete(this.share, 'newPassword')
				this.passwordError = this.isNewShare && this.isPasswordEnforced
				return
			}
			this.passwordError = !this.isValidShareAttribute(password)
			this.$set(this.share, 'newPassword', password)
		},

		/**
		 * Update the password along with "sendPasswordByTalk".
		 *
		 * If the password was modified the new password is sent; otherwise
		 * updating a mail share would fail, as in that case it is required that
		 * a new password is set when enabling or disabling
		 * "sendPasswordByTalk".
		 */
		onPasswordProtectedByTalkChange() {
			if (this.isEmailShareType || this.hasUnsavedPassword) {
				this.queueUpdate('sendPasswordByTalk', 'password')
			} else {
				this.queueUpdate('sendPasswordByTalk')
			}
		},

		isValidShareAttribute(value) {
			if ([null, undefined].includes(value)) {
				return false
			}

			if (!(value.trim().length > 0)) {
				return false
			}

			return true
		},

		getShareTypeIcon(type) {
			switch (type) {
				case ShareType.Link:
					return LinkIcon
				case ShareType.Guest:
					return UserIcon
				case ShareType.RemoteGroup:
				case ShareType.Group:
					return GroupIcon
				case ShareType.Email:
					return EmailIcon
				case ShareType.Team:
					return CircleIcon
				case ShareType.Room:
					return ShareIcon
				case ShareType.Deck:
					return ShareIcon
				case ShareType.ScienceMesh:
					return ShareIcon
				default:
					return null // Or a default icon component if needed
			}
		},
	},
}
</script>

<style lang="scss" scoped>
.sharingTabDetailsView {
	display: flex;
	flex-direction: column;
	width: 100%;
	margin: 0 auto;
	position: relative;
	height: 100%;
	overflow: hidden;

	&__header {
		display: flex;
		align-items: center;
		box-sizing: border-box;
		margin: 0.2em;

		span {
			display: flex;
			align-items: center;

			h1 {
				font-size: 15px;
				padding-inline-start: 0.3em;
			}

		}
	}

	&__wrapper {
		position: relative;
		overflow: scroll;
		flex-shrink: 1;
		padding: 4px;
		padding-inline-end: 12px;
	}

	&__quick-permissions {
		display: flex;
		justify-content: center;
		width: 100%;
		margin: 0 auto;
		border-radius: 0;

		div {
			width: 100%;

			span {
				width: 100%;

				span:nth-child(1) {
					align-items: center;
					justify-content: center;
					padding: 0.1em;
				}

				:deep(label span) {
					display: flex;
					flex-direction: column;
				}

				/* Target component based style in NcCheckboxRadioSwitch slot content*/
				:deep(span.checkbox-content__text.checkbox-radio-switch__text) {
					flex-wrap: wrap;

					.subline {
						display: block;
						flex-basis: 100%;
					}
				}
			}

		}
	}

	&__advanced-control {
		width: 100%;

		button {
			margin-top: 0.5em;
		}

	}

	&__advanced {
		width: 100%;
		margin-bottom: 0.5em;
		text-align: start;
		padding-inline-start: 0;

		section {

			textarea,
			div.mx-datepicker {
				width: 100%;
			}

			textarea {
				height: 80px;
				margin: 0;
			}

			/*
			  The following style is applied out of the component's scope
			  to remove padding from the label.checkbox-radio-switch__label,
			  which is used to group radio checkbox items. The use of ::v-deep
			  ensures that the padding is modified without being affected by
			  the component's scoping.
			  Without this achieving left alignment for the checkboxes would not
			  be possible.
			*/
			span :deep(label) {
				padding-inline-start: 0 !important;
				background-color: initial !important;
				border: none !important;
			}

			section.custom-permissions-group {
				padding-inline-start: 1.5em;
			}
		}
	}

	&__label {
		padding-block-end: 6px;
	}

	&__delete {
		> button:first-child {
			color: rgb(223, 7, 7);
		}
	}

	&__footer {
		width: 100%;
		display: flex;
		position: sticky;
		bottom: 0;
		flex-direction: column;
		justify-content: space-between;
		align-items: flex-start;
		background: linear-gradient(to bottom, rgba(255, 255, 255, 0), var(--color-main-background));

		.button-group {
			display: flex;
			justify-content: space-between;
			width: 100%;
			margin-top: 16px;

			button {
				margin-inline-start: 16px;

				&:first-child {
					margin-inline-start: 0;
				}
			}
		}
	}
}
</style>
